package pl.edu.mimuw.irs.rcp.controller.editor.bucket;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.InputStream;
import java.util.EventObject;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.gef.DefaultEditDomain;
import org.eclipse.gef.EditDomain;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.gef.commands.CommandStackListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.EditorPart;
import org.eclipse.ui.part.FileEditorInput;

import pl.edu.mimuw.irs.core.model.analysis.History;
import pl.edu.mimuw.irs.rcp.controller.editor.bucket.command.AddRun;
import pl.edu.mimuw.irs.rcp.controller.editor.report.Chart;
import pl.edu.mimuw.irs.rcp.controller.editor.report.ResultsTable;
import pl.edu.mimuw.irs.rcp.controller.editor.report.StepsTable;
import pl.edu.mimuw.irs.rcp.persistence.IPersistence;
import pl.edu.mimuw.irs.rcp.persistence.PersistenceAdapter;
import pl.edu.mimuw.irs.rcp.persistence.PersistenceManager;
import pl.edu.mimuw.irs.rcp.util.Logger;
import pl.edu.mimuw.irs.rcp.util.MessageCode;
import pl.edu.mimuw.irs.rcp.util.UITexts;

/**
 * @author Jakub Rauch
 * Created on: 2009-05-25
 */
public class BucketEditor extends EditorPart implements PropertyChangeListener, IPartListener {

	public static final String ID = "pl.edu.mimuw.irs.editor.bucket";
	

	// *** Attributes
	
	private Chart totalCostChart;
	private Chart totalTimeChart;
	private Chart resourcesCostChart;
	private Chart resourcesTimeChart;
	private ResultsTable resultsTable;
	@SuppressWarnings("unused")
	private StepsTable stepsTable;
	private Bucket model;
	private EditDomain editDomain;
	private boolean dirty;
	private ISelectionListener listener = new ISelectionListener() {
		@Override
		public void selectionChanged(IWorkbenchPart part, ISelection selection) {
			if (part != BucketEditor.this) {
			    //showSelection(sourcepart, selection);
			}
		}
	};
	private CommandStackListener commandStackListener = new CommandStackListener() {
		public void commandStackChanged(EventObject event) {
			setDirty(getCommandStack().isDirty());
		}
	};
	public EditDomain getEditDomain() {
		if (editDomain == null) editDomain = new DefaultEditDomain(this);
		return editDomain;
	}
	public void setDirty(boolean dirty) {
		if (this.dirty != dirty) {
			this.dirty = dirty;
			firePropertyChange(IEditorPart.PROP_DIRTY);
		}
	}
	public Bucket getModel() {
		return model;
	}
	public CommandStack getCommandStack() {
		return getEditDomain().getCommandStack();
	}
	protected CommandStackListener getCommandStackListener() {
		return commandStackListener;
	}
	
	// *** Constructors

	public BucketEditor() {
		super();
	}

	// *** Methods

	@Override
	public void doSave(IProgressMonitor monitor) {
		IEditorInput editorInput = getEditorInput();
		
		monitor.beginTask(UITexts.get(MessageCode.BUCKET_SAVE_PROGRESS), IProgressMonitor.UNKNOWN);
		
		if (editorInput instanceof FileEditorInput) {
			FileEditorInput fileEditorInput = (FileEditorInput) editorInput;
			
			IPersistence<Bucket> persistence = PersistenceAdapter.getManager(Bucket.class);
			InputStream source;
			try {
				source = persistence.objectAsInputStream(getModel());
				fileEditorInput.getFile().setContents(source, IResource.FORCE, null);
				
				PersistenceManager manager = PersistenceManager.getInstance(editorInput);
				if (manager == null) return;
				//manager.clearBucket();
			} catch (CoreException e) {
				Logger.handle(MessageCode.BUCKET_SAVE_FILE_PROBLEM, e);
				return;
			} catch (Exception e) {
				Logger.handle(MessageCode.BUCKET_SAVE_SERIALIZE_PROBLEM, e);
				return;
			}
		} else {
			Logger.handle(MessageCode.BUCKET_SAVE_INPUT_PROBLEM, editorInput.getClass(), editorInput.getName());
			return;
		}
		
		monitor.done();

		// update CommandStack
		getCommandStack().markSaveLocation();
	}

	@Override
	public void doSaveAs() {
		
	}

	@Override
	public void init(IEditorSite site, IEditorInput input)
			throws PartInitException {
		setSite(site);
		setInput(input);
		getModel().addPropertyChangeListener(this);
		getCommandStack().addCommandStackListener(commandStackListener);
	}

	@Override
	public void dispose() {
		getModel().removePropertyChangeListener(this);
		getCommandStack().removeCommandStackListener(commandStackListener);
		getSite().getPage().removePartListener(this);
		super.dispose();
	}
	
	@Override
	public boolean isDirty() {
		return this.dirty;
	}

	@Override
	public boolean isSaveAsAllowed() {
		return false;
	}

	/**
	 * This is a callback that will allow us to create the viewer and initialize
	 * it.
	 */
	public void createPartControl(Composite parent) {
		//GridData gridData;
		
	    // create the desired layout for this wizard page
		FillLayout rootLayout = new FillLayout();
		rootLayout.type = SWT.VERTICAL;
		parent.setLayout(rootLayout);

		final List<History> list = getModel();
		//GridData gridData;
		
		/* Prepare data */
		
		BucketCalculations calculations = new BucketCalculations(list);
		
		/* Create total charts */
		
		Group totalCharts = new Group(parent, SWT.NONE);
		totalCharts.setText(UITexts.get(MessageCode.BUCKET_EDITOR_CHART_TOTAL_GROUP));
		FillLayout fillLayout = new FillLayout();
		totalCharts.setLayout(fillLayout);
		
		totalCostChart = new Chart(totalCharts, Chart.VERTICAL_STACKED_BAR_3D,
				UITexts.get(MessageCode.BUCKET_EDITOR_CHART_COST_TOTAL_TITLE),
				UITexts.get(MessageCode.BUCKET_EDITOR_CHART_COST_TOTAL_DOM),
				UITexts.get(MessageCode.BUCKET_EDITOR_CHART_COST_TOTAL_VAL));
		totalCostChart.loadValues(calculations.getTotalCosts());
		totalTimeChart = new Chart(totalCharts, Chart.VERTICAL_STACKED_BAR_3D,
				UITexts.get(MessageCode.BUCKET_EDITOR_CHART_TIME_TOTAL_TITLE),
				UITexts.get(MessageCode.BUCKET_EDITOR_CHART_TIME_TOTAL_DOM),
				UITexts.get(MessageCode.BUCKET_EDITOR_CHART_TIME_TOTAL_VAL));
		totalTimeChart.loadValues(calculations.getTotalTimes());
		
		/* Create resources usage charts */
		
		Group resourcesCharts = new Group(parent, SWT.NONE);
		resourcesCharts.setText(UITexts.get(MessageCode.BUCKET_EDITOR_CHART_RESOURCES_GROUP));
		fillLayout = new FillLayout();
		resourcesCharts.setLayout(fillLayout);
		
		resourcesCostChart = new Chart(resourcesCharts, Chart.VERTICAL_BAR_3D,
				UITexts.get(MessageCode.BUCKET_EDITOR_CHART_COST_RESOURCES_TITLE),
				UITexts.get(MessageCode.BUCKET_EDITOR_CHART_COST_RESOURCES_DOM),
				UITexts.get(MessageCode.BUCKET_EDITOR_CHART_COST_RESOURCES_VAL));
		resourcesCostChart.loadValues(calculations.getResourcesCosts());
		resourcesTimeChart = new Chart(resourcesCharts, Chart.VERTICAL_BAR_3D,
				UITexts.get(MessageCode.BUCKET_EDITOR_CHART_TIME_RESOURCES_TITLE),
				UITexts.get(MessageCode.BUCKET_EDITOR_CHART_TIME_RESOURCES_DOM),
				UITexts.get(MessageCode.BUCKET_EDITOR_CHART_TIME_RESOURCES_VAL));
		resourcesTimeChart.loadValues(calculations.getResourcesTimes());
		
		/* Create table */

		Group details = new Group(parent, SWT.NONE);
		details.setText(UITexts.get(MessageCode.BUCKET_EDITOR_CHART_DETAILS_GROUP));
		fillLayout = new FillLayout();
		details.setLayout(fillLayout);
		
		resultsTable = new ResultsTable(details);
		getSite().setSelectionProvider(resultsTable.getViewer());
		getSite().getWorkbenchWindow().getSelectionService().addSelectionListener(listener);
		stepsTable = new StepsTable(details, resultsTable);

		resultsTable.loadHistory(list);
		
		getSite().getPage().addPartListener(this);
	}
	
	private Map<String, Integer[]> simplify(Map<? extends Object, Integer[]> map) {
		Map<String, Integer[]> newMap = new LinkedHashMap<String, Integer[]>();
		int length = 0;
		for (Integer[] array : map.values()) if (length < array.length) length = array.length;
		
		
		for (Entry<? extends Object, Integer[]> entry : map.entrySet()) {
			Integer[] newMapValues = newMap.get(entry.getKey().toString());
			if (newMapValues == null) {
				newMapValues = new Integer[length];
				newMap.put(entry.getKey().toString(), newMapValues);
			}
			for (int i = 0; i < entry.getValue().length; i++) {
				int value = entry.getValue()[i];
				if (newMapValues[i] == null) newMapValues[i] = 0;
				if (value == 0) continue;
				newMapValues[i] = value;
			}
		}
		return newMap;
	}
	
	

	/**
	 * Passing the focus request to the viewer's control.
	 */
	public void setFocus() {
		resultsTable.getViewer().getControl().setFocus();
	}
	
	@Override
	protected void setInput(IEditorInput input) {
		super.setInput(input);
		
		Bucket bucket = loadBucket(getEditorInput());
		if (bucket == null) return;
		
		model = bucket;
	}
	
	private Bucket loadBucket(IEditorInput input) {
		try {
			PersistenceManager manager = PersistenceManager.getInstance(input);
			if (manager == null) return null;
			return manager.getBucket(false);
		} catch (CoreException e) {
			Logger.handle(MessageCode.BUCKET_LOAD_DESERIALIZE_PROBLEM, e, input);
			return null;
		} catch (Exception e) {
			Logger.handle(MessageCode.BUCKET_LOAD_DESERIALIZE_PROBLEM, e, input);
			return null;
		}
	}
	
	private void closeBucket(IEditorInput input) {
		try {
			PersistenceManager manager = PersistenceManager.getInstance(input);
			if (manager == null) return;
			manager.clearBucket();
		} catch (CoreException e) {
			Logger.handle(MessageCode.BUCKET_CLEAR_PROBLEM, e, input);
		} catch (Exception e) {
			Logger.handle(MessageCode.BUCKET_CLEAR_PROBLEM, e, input);
		}
	}

	@Override
	public void propertyChange(PropertyChangeEvent evt) {
		
		if (evt.getPropertyName().equals(Bucket.RUN_ADDED)) {
			if (evt.getOldValue() == null && evt.getNewValue() != null && evt.getNewValue() instanceof History) {
				History history = (History) evt.getNewValue();
				getCommandStack().execute(new AddRun(getModel(), history));
			}
		}
		
		resultsTable.loadHistory(getModel());
		
		if (getModel().size() == 0) {
			totalCostChart.loadValues(new Integer[]{0});
			totalTimeChart.loadValues(new Integer[]{0});
			resourcesCostChart.loadValues(new Integer[]{0});
			resourcesTimeChart.loadValues(new Integer[]{0});
		} else {
			BucketCalculations calculations = new BucketCalculations(getModel());
			
			totalCostChart.loadValues(calculations.getTotalCosts());
			totalTimeChart.loadValues(calculations.getTotalTimes());
			resourcesCostChart.loadValues(simplify(calculations.getResourcesCosts()));
			resourcesTimeChart.loadValues(simplify(calculations.getResourcesTimes()));
		}
		
	}

	@SuppressWarnings("unchecked")
	@Override
	public Object getAdapter(Class adapter) {
		if (adapter == CommandStack.class)
			return getCommandStack();
		else if (adapter == EditDomain.class)
			return getEditDomain();
		// the super implementation handles the rest
		return super.getAdapter(adapter);
	}
	
	@Override
	public void partActivated(IWorkbenchPart part) {}
	
	@Override
	public void partBroughtToTop(IWorkbenchPart part) {}
	
	@Override
	public void partClosed(IWorkbenchPart part) {
		if (part instanceof BucketEditor) closeBucket(getEditorInput());
	}
	
	@Override
	public void partDeactivated(IWorkbenchPart part) {}
	
	@Override
	public void partOpened(IWorkbenchPart part) {}
}
